#include "UdpServer.h"
#include <cstring>
#include "event2/thread.h"
#if !(defined _WIN32)
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
#endif
#include <iostream>

#define UDPSVR_LOG_FATAL(errnum, fmt, ...) printf("[F] %s:%d [%s] errnum=%d " fmt "\n", __FILE__, __LINE__, __FUNCTION__, errnum, ##__VA_ARGS__)
#define UDPSVR_LOG_ERROR(errnum, fmt, ...) printf("[E] %s:%d [%s] errnum=%d " fmt "\n", __FILE__, __LINE__, __FUNCTION__, errnum, ##__VA_ARGS__)
#define UDPSVR_LOG_WARN(errnum, fmt, ...)  printf("[W] %s:%d [%s] errnum=%d " fmt "\n", __FILE__, __LINE__, __FUNCTION__, errnum, ##__VA_ARGS__)
#define UDPSVR_LOG_INFO(fmt, ...)          printf("[I] %s:%d [%s] " fmt "\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
#define UDPSVR_LOG_DEBUG(fmt, ...)         printf("[D] %s:%d [%s] " fmt "\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
#define UDPSVR_LOG_TRACE(fmt, ...)         printf("[T] %s:%d [%s] " fmt "\n", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)

/**
 * @brief
 * @param[in]
 * @param[out]
 * @retval
 */
UdpServer::UdpServer()
{
    /**
     * 多线程环境下没有调用evthread_use_windows_threads或evthread_use_pthreads函数
     * 会导致event_base_dispatch函数一直阻塞，即使调用了event_base_loopbreak或event_base_loopexit
     * 也无法让event_base_dispatch退出事件循环。
     */
#ifdef _WIN32
    evthread_use_windows_threads();
#else
    evthread_use_pthreads();
#endif
}

/**
 * @brief
 * @param[in]
 * @param[out]
 * @retval
 */
UdpServer::~UdpServer()
{
    /* 停止监听 */
    stop();

    /* 释放资源 */
    deInit();
}

/**
 * @brief      初始化event base,绑定ip、端口
 * @param[in]
 * @param[out]
 * @retval     true:成功 false:失败
 */
bool UdpServer::init(const std::string & addr, int port)
{
    bool ret = true;
    sockaddr_in sin = {0};
    char on = 1;

    /* 上锁 */
    std::lock_guard<std::mutex> locker(m_mutex);

    if(m_isInited)
    {
        UDPSVR_LOG_ERROR(-1, "Alaredy inited, can not init again.");
        return false;
    }

    m_addr = addr;
    m_port = port;

    sin.sin_family = AF_INET;
    sin.sin_port = htons(m_port);
    int retVal = inet_pton(AF_INET, m_addr.c_str(), &sin.sin_addr.s_addr);
    if (1 != retVal)
    {
        UDPSVR_LOG_ERROR(-1, "Invalid ip:%s", m_addr.c_str());
        ret = false;
        goto EXIT;
    }
    m_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(-1 == m_sockfd)
    {
        UDPSVR_LOG_ERROR(-1, "Fail to socket().");
        ret = false;
        goto EXIT;
    }

    setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    if(bind(m_sockfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        UDPSVR_LOG_ERROR(-1, "Fail to bind().");
        ret = false;
        goto EXIT;
    }

    /* 创建libevent的上下文 */
    m_base = event_base_new();
    if(nullptr == m_base)
    {
        UDPSVR_LOG_ERROR(-1, "Fail to event_base_new().");
        ret = false;
        goto EXIT;
    }

    m_event = event_new(m_base, m_sockfd, EV_READ| EV_PERSIST, readCallBack, this);
    if(nullptr == m_event)
    {
        UDPSVR_LOG_ERROR(-1, "Fail to event_new().");
        ret = false;
        goto EXIT;
    }
    event_base_set(m_base, m_event);
    event_add(m_event, nullptr);

    m_isInited = true;
    UDPSVR_LOG_WARN(0, "%s:%d init ok", m_addr.c_str(), m_port);

EXIT:
    if(!ret)
    {
        if(m_event)
        {
            event_free(m_event);
            m_event = nullptr;
        }
        if(m_base)
        {
            event_base_free(m_base);
            m_base = nullptr;
        }
        if(m_sockfd)
        {
            closeSocket(m_sockfd);
            m_sockfd = -1;
        }
    }
    return ret;
}

/**
 * @brief      释放资源
 * @param[in]
 * @param[out]
 * @retval    true:成功 false:失败
 */
bool UdpServer::deInit()
{
    /* 上锁 */
    std::lock_guard<std::mutex> locker(m_mutex);

    if(m_isRunning)
    {
        UDPSVR_LOG_ERROR(-1, "Uninit fail, do stop before deInit.");
        return false;
    }

    if(false == m_isInited)
    {
        return true;
    }

    if(m_event)
    {
        event_free(m_event);
        m_event = nullptr;
    }
    if(m_base)
    {
        event_base_free(m_base);
        m_base = nullptr;
    }
    if(m_sockfd)
    {
        closeSocket(m_sockfd);
        m_sockfd = -1;
    }

    m_isInited = false;
    return true;
}

/**
 * @brief      bufferevent读回调函数
 * @param[in]
 * @param[out]
 * @retval
 */
void UdpServer::readCallBack(evutil_socket_t sockfd, short event, void* arg)
{
    UdpServer * pThis = reinterpret_cast <UdpServer *>(arg);

    unsigned char buf[UDP_SERVER_BUF_SIZE] = {0};
    struct sockaddr_in clientaddr = {0};
    socklen_t addrlen = sizeof(clientaddr);
    int ret = 0;

    if((ret = recvfrom(pThis->m_sockfd, (char *)buf, sizeof(buf), 0, (struct sockaddr *)&clientaddr, &addrlen)) < 0)
    {
        UDPSVR_LOG_ERROR(-1, "fail to recvfrom, ret = %d", ret);
    }
    else
    {
#if 0
        char addr[16] = {0};
        inet_ntop(AF_INET, &clientaddr.sin_addr, addr, sizeof(addr));
        UDPSVR_LOG_INFO("Recv from %s:%d", addr, ntohs(clientaddr.sin_port));
        UDPSVR_LOG_INFO("data: %s", buf);
#endif
        if(pThis->m_readHandler)
        {
            pThis->m_readHandler(clientaddr, buf, ret, pThis->m_callbackArg);
        }
    }
}

/**
 * @brief     启动监听新连接
 * @param[in]
 * @param[out]
 * @retval    true:成功 false:失败
 */
bool UdpServer::start()
{
    bool ret = true;

    /* 上锁 */
    std::lock_guard<std::mutex> locker(m_mutex);
    if(!m_isInited)
    {
        UDPSVR_LOG_ERROR(-1, "Start error, no init.");
        ret = false;
        goto EXIT;
    }

    if(m_isRunning)
    {
        UDPSVR_LOG_ERROR(-1, "Alaredy start, can not start again.");
        ret = false;
        goto EXIT;
    }

    /* 设置运行标志位在创建线程之前 */
    m_isRunning = true;
    m_thread = new (std::nothrow) std::thread(dispatchThread, this);
    if(nullptr == m_thread)
    {
        UDPSVR_LOG_ERROR(0, "Poll thread create fail");
        m_isRunning = false;
        ret = false;
        goto EXIT;
    }

    UDPSVR_LOG_WARN(0, "%s:%d start ok", m_addr.c_str(), m_port);

EXIT:
    return ret;
}

/**
 * @brief     停止监听新连接
 * @param[in]
 * @param[out]
 * @retval    true:成功 false:失败
 */
bool UdpServer::stop()
{
    /* 上锁 */
    std::lock_guard<std::mutex> locker(m_mutex);

    if(m_isRunning)
    {
        m_isRunning = false;
        event_base_loopbreak(m_base);
        event_base_loopexit(m_base, nullptr);
        if(m_thread)
        {
            m_thread->join();
            delete m_thread;
            m_thread = nullptr;
        }
        UDPSVR_LOG_WARN(0, "%s:%d stopped", m_addr.c_str(), m_port);
    }
    return (false == m_isRunning);
}

/**
 * @brief      设置读到报警消息的回调函数
 * @param[in]
 * @param[out]
 * @retval
 */
bool UdpServer::setReadHandler(const ReadHandler & readCb, void * arg)
{
    /* 上锁 */
    std::lock_guard<std::mutex> locker(m_mutex);

    if(readCb)
    {
        m_readHandler = readCb;
        m_callbackArg = arg;
    }
    return (m_readHandler != nullptr);
}

/**
 * @brief     dispatch线程函数
 * @param[in]
 * @param[out]
 * @retval  void *
 */
void *UdpServer::dispatchThread(void * arg)
{
    UdpServer * pThis = reinterpret_cast<UdpServer *>(arg);

    if(pThis->m_base)
    {
        event_base_dispatch(pThis->m_base);
    }

    UDPSVR_LOG_WARN(0, "UdpServer server poll thread exit.\n");
    return nullptr;
}

/**
 * @brief     关闭socket
 * @param[in] sockfd : socket文件描述符
 * @retval  void 
 */
void UdpServer::closeSocket(int sockfd)
{
#ifdef _WIN32
    closesocket(m_sockfd);
#else
    close(m_sockfd);
#endif
}
